package com.sunny.common.redisson.config;

import com.google.common.collect.Maps;
import com.sunny.common.redisson.constant.RedissonConstant;
import com.sunny.common.redisson.properties.MultipleRedissonProperties;
import com.sunny.common.redisson.properties.MultipleServerConfig;
import com.sunny.common.redisson.properties.RedissonProperties;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReplicatedServersConfig;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.connection.balancer.LoadBalancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.Map;

@Configuration
@ConditionalOnProperty(prefix = "redisson.open", name = {"flag"}, havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(value = {RedissonProperties.class, MultipleRedissonProperties.class})
@ConditionalOnClass(MultipleRedissonProperties.class)
public class RedissonConfiguration {
    private final String REDIS = "redis";

    @Autowired
    private RedissonProperties redissonProperties;

    @Autowired
    private MultipleRedissonProperties multipleRedissonProperties;

    @Bean
    @ConditionalOnMissingBean(MultipleRedissonClient.class)
    public MultipleRedissonClient multipleRedissonClient() {
        Map<String, RedissonProperties> properties = multipleRedissonProperties.getSources();
        if (properties == null) {
            properties = Maps.newHashMap();
        }
        // 默认redis配置
        properties.put(RedissonConstant.REDIS_DEFAULT_NAME, redissonProperties);
        Assert.notEmpty(properties, "redisson configuration is empty");
        MultipleRedissonClient client = new MultipleRedissonClient();
        Map<String, RedissonClient> clientMap = Maps.newHashMapWithExpectedSize(properties.size());
        client.setClientMap(clientMap);
        properties.forEach((name, redissonProperties) -> {
            Config config = new Config();
            try {
                config.setCodec((Codec)Class.forName(redissonProperties.getCodec()).newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            config.setTransportMode(redissonProperties.getTransportMode());
            if (redissonProperties.getThreads() != null) {
                config.setThreads(redissonProperties.getThreads());
            }
            if (redissonProperties.getNettyThreads() != null) {
                config.setNettyThreads(redissonProperties.getNettyThreads());
            }
            config.setReferenceEnabled(redissonProperties.getReferenceEnabled());
            config.setLockWatchdogTimeout(redissonProperties.getLockWatchdogTimeout());
            config.setKeepPubSubOrder(redissonProperties.getKeepPubSubOrder());
            config.setUseScriptCache(redissonProperties.getUseScriptCache());
            config.setMinCleanUpDelay(redissonProperties.getMinCleanUpDelay());
            config.setMaxCleanUpDelay(redissonProperties.getMaxCleanUpDelay());

            MultipleServerConfig multipleServerConfig = redissonProperties.getMultipleServerConfig();

            switch (redissonProperties.getModel()) {
                case SINGLE:
                    SingleServerConfig singleServerConfig = config.useSingleServer();
                    com.sunny.common.redisson.properties.SingleServerConfig param = redissonProperties.getSingleServerConfig();
                    singleServerConfig.setAddress(prefixAddress(param.getAddress()));
                    singleServerConfig.setConnectionMinimumIdleSize(param.getConnectionMinimumIdleSize());
                    singleServerConfig.setConnectionPoolSize(param.getConnectionPoolSize());
                    singleServerConfig.setDatabase(param.getDatabase());
                    singleServerConfig.setDnsMonitoringInterval(param.getDnsMonitoringInterval());
                    singleServerConfig
                            .setSubscriptionConnectionMinimumIdleSize(param.getSubscriptionConnectionMinimumIdleSize());
                    singleServerConfig.setSubscriptionConnectionPoolSize(param.getSubscriptionConnectionPoolSize());
                    singleServerConfig.setClientName(redissonProperties.getClientName());
                    singleServerConfig.setConnectTimeout(redissonProperties.getConnectTimeout());
                    singleServerConfig.setIdleConnectionTimeout(redissonProperties.getIdleConnectionTimeout());
                    singleServerConfig.setKeepAlive(redissonProperties.getKeepAlive());
                    singleServerConfig.setPassword(redissonProperties.getPassword());
                    singleServerConfig.setPingConnectionInterval(redissonProperties.getPingConnectionInterval());
                    singleServerConfig.setRetryAttempts(redissonProperties.getRetryAttempts());
                    singleServerConfig.setRetryInterval(redissonProperties.getRetryInterval());
                    singleServerConfig
                            .setSslEnableEndpointIdentification(redissonProperties.getSslEnableEndpointIdentification());
                    singleServerConfig.setSslKeystore(redissonProperties.getSslKeystore());
                    singleServerConfig.setSslKeystorePassword(redissonProperties.getSslKeystorePassword());
                    singleServerConfig.setSslProvider(redissonProperties.getSslProvider());
                    singleServerConfig.setSslTruststore(redissonProperties.getSslTruststore());
                    singleServerConfig.setSslTruststorePassword(redissonProperties.getSslTruststorePassword());
                    singleServerConfig.setSubscriptionsPerConnection(redissonProperties.getSubscriptionsPerConnection());
                    singleServerConfig.setTcpNoDelay(redissonProperties.getTcpNoDelay());
                    singleServerConfig.setTimeout(redissonProperties.getTimeout());
                    break;
                case CLUSTER:
                    ClusterServersConfig clusterServersConfig = config.useClusterServers();
                    clusterConfig(redissonProperties, multipleServerConfig, clusterServersConfig);
                    break;
                case SENTINEL:
                    SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
                    sentinelConfig(redissonProperties, multipleServerConfig, sentinelServersConfig);
                    break;
                case REPLICATED:
                    replicatedConfig(redissonProperties, config, multipleServerConfig);
                    break;
                case MASTERSLAVE:
                    MasterSlaveServersConfig masterSlaveServersConfig = config.useMasterSlaveServers();
                    masterSlaveConfig(redissonProperties, multipleServerConfig, masterSlaveServersConfig);
                    break;
                default:
                    break;

            }
            clientMap.put(name, Redisson.create(config));
        });
        return client;
    }

    private void clusterConfig(RedissonProperties redissonProperties, MultipleServerConfig multipleServerConfig, ClusterServersConfig clusterServersConfig) {
        clusterServersConfig.setScanInterval(multipleServerConfig.getScanInterval());
        clusterServersConfig
                .setSlaveConnectionMinimumIdleSize(multipleServerConfig.getSlaveConnectionMinimumIdleSize());
        clusterServersConfig.setSlaveConnectionPoolSize(multipleServerConfig.getSlaveConnectionPoolSize());
        clusterServersConfig
                .setFailedSlaveReconnectionInterval(multipleServerConfig.getFailedSlaveReconnectionInterval());
        clusterServersConfig.setFailedSlaveCheckInterval(multipleServerConfig.getFailedSlaveCheckInterval());
        clusterServersConfig
                .setMasterConnectionMinimumIdleSize(multipleServerConfig.getMasterConnectionMinimumIdleSize());
        clusterServersConfig.setMasterConnectionPoolSize(multipleServerConfig.getMasterConnectionPoolSize());
        clusterServersConfig.setReadMode(multipleServerConfig.getReadMode());
        clusterServersConfig.setSubscriptionMode(multipleServerConfig.getSubscriptionMode());
        clusterServersConfig.setSubscriptionConnectionMinimumIdleSize(
                multipleServerConfig.getSubscriptionConnectionMinimumIdleSize());
        clusterServersConfig
                .setSubscriptionConnectionPoolSize(multipleServerConfig.getSubscriptionConnectionPoolSize());
        clusterServersConfig.setDnsMonitoringInterval(multipleServerConfig.getDnsMonitoringInterval());
        try {
            clusterServersConfig.setLoadBalancer(
                    (LoadBalancer)Class.forName(multipleServerConfig.getLoadBalancer()).newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        for (String nodeAddress : multipleServerConfig.getNodeAddresses()) {
            clusterServersConfig.addNodeAddress(prefixAddress(nodeAddress));
        }
        clusterServersConfig.setClientName(redissonProperties.getClientName());
        clusterServersConfig.setConnectTimeout(redissonProperties.getConnectTimeout());
        clusterServersConfig.setIdleConnectionTimeout(redissonProperties.getIdleConnectionTimeout());
        clusterServersConfig.setKeepAlive(redissonProperties.getKeepAlive());
        clusterServersConfig.setPassword(redissonProperties.getPassword());
        clusterServersConfig.setPingConnectionInterval(redissonProperties.getPingConnectionInterval());
        clusterServersConfig.setRetryAttempts(redissonProperties.getRetryAttempts());
        clusterServersConfig.setRetryInterval(redissonProperties.getRetryInterval());
        clusterServersConfig
                .setSslEnableEndpointIdentification(redissonProperties.getSslEnableEndpointIdentification());
        clusterServersConfig.setSslKeystore(redissonProperties.getSslKeystore());
        clusterServersConfig.setSslKeystorePassword(redissonProperties.getSslKeystorePassword());
        clusterServersConfig.setSslProvider(redissonProperties.getSslProvider());
        clusterServersConfig.setSslTruststore(redissonProperties.getSslTruststore());
        clusterServersConfig.setSslTruststorePassword(redissonProperties.getSslTruststorePassword());
        clusterServersConfig.setSubscriptionsPerConnection(redissonProperties.getSubscriptionsPerConnection());
        clusterServersConfig.setTcpNoDelay(redissonProperties.getTcpNoDelay());
        clusterServersConfig.setTimeout(redissonProperties.getTimeout());
    }

    private void sentinelConfig(RedissonProperties redissonProperties, MultipleServerConfig multipleServerConfig, SentinelServersConfig sentinelServersConfig) {
        sentinelServersConfig.setCheckSentinelsList(false);
        sentinelServersConfig.setDatabase(multipleServerConfig.getDatabase());
        sentinelServersConfig.setMasterName(multipleServerConfig.getMasterName());
        sentinelServersConfig.setScanInterval(multipleServerConfig.getScanInterval());
        sentinelServersConfig
                .setSlaveConnectionMinimumIdleSize(multipleServerConfig.getSlaveConnectionMinimumIdleSize());
        sentinelServersConfig.setSlaveConnectionPoolSize(multipleServerConfig.getSlaveConnectionPoolSize());
        sentinelServersConfig
                .setFailedSlaveReconnectionInterval(multipleServerConfig.getFailedSlaveReconnectionInterval());
        sentinelServersConfig.setFailedSlaveCheckInterval(multipleServerConfig.getFailedSlaveCheckInterval());
        sentinelServersConfig
                .setMasterConnectionMinimumIdleSize(multipleServerConfig.getMasterConnectionMinimumIdleSize());
        sentinelServersConfig.setMasterConnectionPoolSize(multipleServerConfig.getMasterConnectionPoolSize());
        sentinelServersConfig.setReadMode(multipleServerConfig.getReadMode());
        sentinelServersConfig.setSubscriptionMode(multipleServerConfig.getSubscriptionMode());
        sentinelServersConfig.setSubscriptionConnectionMinimumIdleSize(
                multipleServerConfig.getSubscriptionConnectionMinimumIdleSize());
        sentinelServersConfig
                .setSubscriptionConnectionPoolSize(multipleServerConfig.getSubscriptionConnectionPoolSize());
        sentinelServersConfig.setDnsMonitoringInterval(multipleServerConfig.getDnsMonitoringInterval());
        try {
            sentinelServersConfig.setLoadBalancer(
                    (LoadBalancer)Class.forName(multipleServerConfig.getLoadBalancer()).newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        for (String nodeAddress : multipleServerConfig.getNodeAddresses()) {
            sentinelServersConfig.addSentinelAddress(prefixAddress(nodeAddress));
        }
        sentinelServersConfig.setClientName(redissonProperties.getClientName());
        sentinelServersConfig.setConnectTimeout(redissonProperties.getConnectTimeout());
        sentinelServersConfig.setIdleConnectionTimeout(redissonProperties.getIdleConnectionTimeout());
        sentinelServersConfig.setKeepAlive(redissonProperties.getKeepAlive());
        sentinelServersConfig.setPassword(redissonProperties.getPassword());
        sentinelServersConfig.setPingConnectionInterval(redissonProperties.getPingConnectionInterval());
        sentinelServersConfig.setRetryAttempts(redissonProperties.getRetryAttempts());
        sentinelServersConfig.setRetryInterval(redissonProperties.getRetryInterval());
        sentinelServersConfig
                .setSslEnableEndpointIdentification(redissonProperties.getSslEnableEndpointIdentification());
        sentinelServersConfig.setSslKeystore(redissonProperties.getSslKeystore());
        sentinelServersConfig.setSslKeystorePassword(redissonProperties.getSslKeystorePassword());
        sentinelServersConfig.setSslProvider(redissonProperties.getSslProvider());
        sentinelServersConfig.setSslTruststore(redissonProperties.getSslTruststore());
        sentinelServersConfig.setSslTruststorePassword(redissonProperties.getSslTruststorePassword());
        sentinelServersConfig.setSubscriptionsPerConnection(redissonProperties.getSubscriptionsPerConnection());
        sentinelServersConfig.setTcpNoDelay(redissonProperties.getTcpNoDelay());
        sentinelServersConfig.setTimeout(redissonProperties.getTimeout());
    }

    private void replicatedConfig(RedissonProperties redissonProperties, Config config, MultipleServerConfig multipleServerConfig) {
        ReplicatedServersConfig replicatedServersConfig = config.useReplicatedServers();
        replicatedServersConfig.setDatabase(multipleServerConfig.getDatabase());
        replicatedServersConfig.setScanInterval(multipleServerConfig.getScanInterval());
        replicatedServersConfig
                .setSlaveConnectionMinimumIdleSize(multipleServerConfig.getSlaveConnectionMinimumIdleSize());
        replicatedServersConfig.setSlaveConnectionPoolSize(multipleServerConfig.getSlaveConnectionPoolSize());
        replicatedServersConfig
                .setFailedSlaveReconnectionInterval(multipleServerConfig.getFailedSlaveReconnectionInterval());
        replicatedServersConfig.setFailedSlaveCheckInterval(multipleServerConfig.getFailedSlaveCheckInterval());
        replicatedServersConfig
                .setMasterConnectionMinimumIdleSize(multipleServerConfig.getMasterConnectionMinimumIdleSize());
        replicatedServersConfig.setMasterConnectionPoolSize(multipleServerConfig.getMasterConnectionPoolSize());
        replicatedServersConfig.setReadMode(multipleServerConfig.getReadMode());
        replicatedServersConfig.setSubscriptionMode(multipleServerConfig.getSubscriptionMode());
        replicatedServersConfig.setSubscriptionConnectionMinimumIdleSize(
                multipleServerConfig.getSubscriptionConnectionMinimumIdleSize());
        replicatedServersConfig
                .setSubscriptionConnectionPoolSize(multipleServerConfig.getSubscriptionConnectionPoolSize());
        replicatedServersConfig.setDnsMonitoringInterval(multipleServerConfig.getDnsMonitoringInterval());
        try {
            replicatedServersConfig.setLoadBalancer(
                    (LoadBalancer)Class.forName(multipleServerConfig.getLoadBalancer()).newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        for (String nodeAddress : multipleServerConfig.getNodeAddresses()) {
            replicatedServersConfig.addNodeAddress(prefixAddress(nodeAddress));
        }
        replicatedServersConfig.setClientName(redissonProperties.getClientName());
        replicatedServersConfig.setConnectTimeout(redissonProperties.getConnectTimeout());
        replicatedServersConfig.setIdleConnectionTimeout(redissonProperties.getIdleConnectionTimeout());
        replicatedServersConfig.setKeepAlive(redissonProperties.getKeepAlive());
        replicatedServersConfig.setPassword(redissonProperties.getPassword());
        replicatedServersConfig.setPingConnectionInterval(redissonProperties.getPingConnectionInterval());
        replicatedServersConfig.setRetryAttempts(redissonProperties.getRetryAttempts());
        replicatedServersConfig.setRetryInterval(redissonProperties.getRetryInterval());
        replicatedServersConfig
                .setSslEnableEndpointIdentification(redissonProperties.getSslEnableEndpointIdentification());
        replicatedServersConfig.setSslKeystore(redissonProperties.getSslKeystore());
        replicatedServersConfig.setSslKeystorePassword(redissonProperties.getSslKeystorePassword());
        replicatedServersConfig.setSslProvider(redissonProperties.getSslProvider());
        replicatedServersConfig.setSslTruststore(redissonProperties.getSslTruststore());
        replicatedServersConfig.setSslTruststorePassword(redissonProperties.getSslTruststorePassword());
        replicatedServersConfig
                .setSubscriptionsPerConnection(redissonProperties.getSubscriptionsPerConnection());
        replicatedServersConfig.setTcpNoDelay(redissonProperties.getTcpNoDelay());
        replicatedServersConfig.setTimeout(redissonProperties.getTimeout());
    }

    private void masterSlaveConfig(RedissonProperties redissonProperties, MultipleServerConfig multipleServerConfig, MasterSlaveServersConfig masterSlaveServersConfig) {
        masterSlaveServersConfig.setDatabase(multipleServerConfig.getDatabase());
        masterSlaveServersConfig
                .setSlaveConnectionMinimumIdleSize(multipleServerConfig.getSlaveConnectionMinimumIdleSize());
        masterSlaveServersConfig.setSlaveConnectionPoolSize(multipleServerConfig.getSlaveConnectionPoolSize());
        masterSlaveServersConfig
                .setFailedSlaveReconnectionInterval(multipleServerConfig.getFailedSlaveReconnectionInterval());
        masterSlaveServersConfig
                .setFailedSlaveCheckInterval(multipleServerConfig.getFailedSlaveCheckInterval());
        masterSlaveServersConfig
                .setMasterConnectionMinimumIdleSize(multipleServerConfig.getMasterConnectionMinimumIdleSize());
        masterSlaveServersConfig
                .setMasterConnectionPoolSize(multipleServerConfig.getMasterConnectionPoolSize());
        masterSlaveServersConfig.setReadMode(multipleServerConfig.getReadMode());
        masterSlaveServersConfig.setSubscriptionMode(multipleServerConfig.getSubscriptionMode());
        masterSlaveServersConfig.setSubscriptionConnectionMinimumIdleSize(
                multipleServerConfig.getSubscriptionConnectionMinimumIdleSize());
        masterSlaveServersConfig
                .setSubscriptionConnectionPoolSize(multipleServerConfig.getSubscriptionConnectionPoolSize());
        masterSlaveServersConfig.setDnsMonitoringInterval(multipleServerConfig.getDnsMonitoringInterval());
        try {
            masterSlaveServersConfig.setLoadBalancer(
                    (LoadBalancer)Class.forName(multipleServerConfig.getLoadBalancer()).newInstance());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        int index = 0;
        for (String nodeAddress : multipleServerConfig.getNodeAddresses()) {
            if (index++ == 0) {
                masterSlaveServersConfig.setMasterAddress(prefixAddress(nodeAddress));
            } else {
                masterSlaveServersConfig.addSlaveAddress(prefixAddress(nodeAddress));
            }
        }
        masterSlaveServersConfig.setClientName(redissonProperties.getClientName());
        masterSlaveServersConfig.setConnectTimeout(redissonProperties.getConnectTimeout());
        masterSlaveServersConfig.setIdleConnectionTimeout(redissonProperties.getIdleConnectionTimeout());
        masterSlaveServersConfig.setKeepAlive(redissonProperties.getKeepAlive());
        masterSlaveServersConfig.setPassword(redissonProperties.getPassword());
        masterSlaveServersConfig.setPingConnectionInterval(redissonProperties.getPingConnectionInterval());
        masterSlaveServersConfig.setRetryAttempts(redissonProperties.getRetryAttempts());
        masterSlaveServersConfig.setRetryInterval(redissonProperties.getRetryInterval());
        masterSlaveServersConfig
                .setSslEnableEndpointIdentification(redissonProperties.getSslEnableEndpointIdentification());
        masterSlaveServersConfig.setSslKeystore(redissonProperties.getSslKeystore());
        masterSlaveServersConfig.setSslKeystorePassword(redissonProperties.getSslKeystorePassword());
        masterSlaveServersConfig.setSslProvider(redissonProperties.getSslProvider());
        masterSlaveServersConfig.setSslTruststore(redissonProperties.getSslTruststore());
        masterSlaveServersConfig.setSslTruststorePassword(redissonProperties.getSslTruststorePassword());
        masterSlaveServersConfig
                .setSubscriptionsPerConnection(redissonProperties.getSubscriptionsPerConnection());
        masterSlaveServersConfig.setTcpNoDelay(redissonProperties.getTcpNoDelay());
        masterSlaveServersConfig.setTimeout(redissonProperties.getTimeout());
    }

    private String prefixAddress(String address) {
        if (!StringUtils.isEmpty(address) && !address.startsWith(REDIS)) {
            return "redis://" + address;
        }
        return address;
    }

}
